iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
Software Development

深入淺出Java 30天系列 第 24

Day 24: 偏好使用interfaces而不是抽象類別(abstract class)(下)

  • 分享至 

  • xImage
  •  

雖然昨天說了很多應該用interface,不建議用抽象類別的原因,但interface有個缺點:無法重複使用程式碼,如果想要有interface可mixins的優點,又要有抽象類別可以讓子類別重複使用程式碼的優勢,可以提供abstract skeletal implementation

skeletal implementation也被稱為AbstractInterface,collection架構有很多這種設計,像是AbstractCollectionAbstractSet...,使用上非常方便,只要實作沒有被實作的抽象方法,就能夠使用了。

import java.util.AbstractList;
import java.util.List;
public class ConvertType {
    static List<Integer> intArrayAsList(final int[] a) {
        if (a == null) {
            throw new NullPointerException();
        }
        return new AbstractList<Integer>() {
            @Override
            public Integer get(int index) {
                return a[index];
            }
            @Override
            public int size() {
                return a.length;
            }
        };
    }

    public static void main(String[] args) {
        int[] myArray = {1, 2, 3};
        List<Integer> myList = intArrayAsList(myArray);
        System.out.println(myList);
    }
}

使用簡單,要自行設計skeletal implementation也不難,只要設計一個抽象類別,並實作interface,就完成skeletal implementation,流程上會像下面這張圖,中間的抽象類別其實就像Day 21: 最好使用composition而不是繼承(下)介紹過的forwarding class

使用的時候,可以參考上面AbstractList的範例,以匿名類別(Anonymous class)的方式使用,也可以參考下面範例,新增一個類別,在用繼承抽象類別的方式來使用,當然,不管用哪一種方式,都是離不開繼承,所以設計skeletal implementation時,務必要遵守Day 22: 設計並記錄繼承的使用方式,否則禁止繼承的原則。

// 定義一個接口
interface Animal {
    void eat();
    void sleep();
}

// 定義一個抽象類實現該接口
abstract class Mammal implements Animal {
    // 提供接口方法的一部分實現
    @Override
    public void sleep() {
        System.out.println("Mammal is sleeping");
    }

    // 抽象方法,留給子類實現
    @Override
    public abstract void eat();
}

// 具體子類實現剩餘的抽象方法
public class Dog extends Mammal {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }

    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();   // 輸出: Dog is eating
        dog.sleep(); // 輸出: Mammal is sleeping
    }
}

skeletal implementation除了可以結合interface和抽象類別的優點之外,還有另外一個優點是,如果要在既有的interface加方法,或需要實作新的interface,可以在抽象類別做調整,不會影響繼承抽象類別的類別。但如果類別是自己實作interface,不是透過抽象類別做使用,interface幾乎不可能加方法,因為這個會造成很多實作該interface的類別,無法compile成功。

說了這麼多skeletal implementation的優點,最後還是要特別提醒,避免濫用skeletal implementation,因為當越來越多類別繼承抽象類別,哪天要改寫時,會發現影響的範圍很大,終究還是回到Day 20: 最好使用composition而不是繼承(上)說的那些老問題。


上一篇
Day 23: 偏好使用interface而不是抽象類別(abstract class)(上)
下一篇
Day 25: Nested class的四種類型(上)
系列文
深入淺出Java 30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言